using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;

namespace CoolCode.Web.Mvc.UI.Grid
{
    /// <summary>
    /// Base class for Grid Renderers. 
    /// </summary>
    public abstract class GridRenderer<T> : IGridRenderer<T> where T : class
    {
        protected IGridModel<T> GridModel { get; private set; }
        protected IEnumerable<T> DataSource { get; set; }
        protected ViewContext Context { get; private set; }
        protected TextWriter Writer { get; private set; }
        private readonly ViewEngineCollection _engines;

		protected DomBuilder Dom = new DomBuilder();

        protected GridRenderer() : this(ViewEngines.Engines) { }

        protected GridRenderer(ViewEngineCollection engines)
        {
            _engines = engines;
        }

        public void Render(IGridModel<T> gridModel, IEnumerable<T> dataSource, TextWriter output, ViewContext context)
        {
            Writer = output; 

            GridModel = gridModel;
            DataSource = dataSource;
            Context = context;

            RenderGridStart();

			if (IsDataSourceEmpty())
			{
				RenderEmpty();
			}
			else
			{
				RenderTable();
			}

            RenderGridEnd( );
        }

        protected void RenderText(string text)
        {
            //Writer.Write(text);
			Dom.Write(text);
        }

		protected virtual void RenderTable()
		{
			RenderTableStart();
            RenderHeader(); 
            RenderItems();
			RenderTableEnd();
		}

        protected virtual void RenderItems()
        {
            RenderBodyStart();

            int rows = 0;
            bool isAlternate = false;
            foreach (var item in DataSource)
            {
                RenderItem(new GridRowViewData<T>(item, isAlternate));
                isAlternate = !isAlternate;
                rows++;
            }
			/*remove blank row -- 2010-07-23
            //Add blank row when display rows less then page size.-- 2010-02-01
            IPageable pagerSource = DataSource as IPageable;
            if (pagerSource != null)
            {
                RenderEmptyRow(pagerSource.PageSize - rows);
            }
			*/
            RenderBodyEnd();
        }

        protected virtual void RenderItem(GridRowViewData<T> rowData)
        {
            BaseRenderRowStart(rowData);

            foreach (var column in VisibleColumns())
            {
                //A custom item section has been specified - render it and continue to the next iteration.
                RenderStartCell(column, rowData);
				if (column.CustomItemRenderer != null)
				{
					column.CustomItemRenderer(new RenderingContext(Dom, Context, _engines), rowData.Item);
				}
				else
				{
					RenderCellValue(column, rowData);
				}
                RenderEndCell();
            }

            BaseRenderRowEnd(rowData);
        }

        protected virtual void RenderCellValue(GridColumn<T> column, GridRowViewData<T> rowData)
        {
            //Add hyperlink for click to edit. -- 2010-02-01
			//bool linkToEdit = (column.Name == "Name");
			//if (linkToEdit)
			//{
			//    string id =string.Empty ;
			//    IEntity entity = rowData.Item as IEntity;
			//    if (entity != null)
			//    {
			//        id = entity.ID .ToString();
			//    }
			//    //RenderText(string.Format("<a href=\"#\" onclick=\"$('#{1}').edit('{0}');\">", id, GridModel.ID ));
			//    RenderText("<div class=\"editRow\">");
			//    RenderText(string.Format("<a href=\"#\">", id, GridModel.ID));
			//}

            var cellValue = column.GetValue(rowData.Item);

            if (cellValue != null)
            {
                RenderText(cellValue.ToString());
            }

            //Add hyperlink for click to edit. -- 2010-02-01
			//if (linkToEdit)
			//{
			//    RenderText("</a>");
			//    RenderText("</div>");
			//}
        }

        protected virtual bool RenderHeader()
        { 
            RenderHeadStart();

            foreach (var column in VisibleColumns())
            {
                //Allow for custom header overrides.
				//if (column.CustomHeaderRenderer != null)
				//{
				//    RenderHeaderCellStart(column);
				//    column.CustomHeaderRenderer(new RenderingContext(Dom, Context, _engines));
				//    RenderHeaderCellEnd();
				//}
				//else
				//{
                    RenderHeaderCellStart(column);
                    RenderHeaderText(column);
                    RenderHeaderCellEnd();
               // }
            }

            RenderHeadEnd();

            return true;
        }

        protected virtual void RenderHeaderText(GridColumn<T> column)
        {
            RenderText(column.DisplayName);
        }

        protected virtual bool ShouldRenderHeader()
        {
            return !IsDataSourceEmpty();
        }

        protected bool IsDataSourceEmpty()
        {
            return DataSource == null || !DataSource.Any();
        }

        protected IEnumerable<GridColumn<T>> VisibleColumns()
        {
            return GridModel.Columns.Where(x => x.Visible);
        }

        protected void BaseRenderRowStart(GridRowViewData<T> rowData)
        {
            bool rendered = GridModel.Sections.Row.StartSectionRenderer(rowData, new RenderingContext(Dom, Context, _engines));

            if (!rendered)
            {
                RenderRowStart(rowData);
            }
        }

        protected void BaseRenderRowEnd(GridRowViewData<T> rowData)
        {
            bool rendered = GridModel.Sections.Row.EndSectionRenderer(rowData, new RenderingContext(Dom, Context, _engines));

            if (!rendered)
            {
                RenderRowEnd();
            }
        }

		protected abstract void RenderTableStart();
		protected abstract void RenderTableEnd();
        protected abstract void RenderHeaderCellStart(GridColumn<T> column);
        protected abstract void RenderHeaderCellEnd();
        protected abstract void RenderRowStart(GridRowViewData<T> rowData);
        protected abstract void RenderRowEnd();
        protected abstract void RenderEndCell();
        protected abstract void RenderStartCell(GridColumn<T> column, GridRowViewData<T> rowViewData);
        protected abstract void RenderHeadStart();
        protected abstract void RenderHeadEnd();
        protected abstract void RenderGridStart();
        protected abstract void RenderGridEnd( );
        protected abstract void RenderEmpty();
        protected abstract void RenderBodyStart();
        protected abstract void RenderBodyEnd();
    }
}
